package com.free.bsf.health.memoryParse;

import com.free.bsf.core.base.BsfException;
import com.free.bsf.core.util.LogUtils;
import com.free.bsf.core.util.PropertyUtils;
import com.free.bsf.health.config.HealthProperties;
import lombok.val;

import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Classname PhantomHashMap
 * @Description 引用计数器
 * @Date 2021/6/7 11:07
 * @Created by chejiangyi
 */
public class ReferenceHashMap {
    private final ConcurrentHashMap<String, List<Reference>> map = new ConcurrentHashMap<>();
    //private ConcurrentHashMap<Long, List<Object>> references = new ConcurrentHashMap();
    private final ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
    private final Object lock=new Object();
    private Field referentField=null;


    public ReferenceHashMap(){
        try {
            //Collections.synchronizedList(new ArrayList<>()).addAll();
            referentField = Reference.class.getDeclaredField("referent");
            referentField.setAccessible(true);
        }catch (Exception e){
            throw new BsfException(e);
        }
        Thread thread = new Thread(()->{
            clearUpReference(true);
        });
        thread.setDaemon(true);
        thread.start();
    }
    public void put(ObjParser.ObjInfo objInfo){
        //clearUpReference(false);
        if (!PropertyUtils.getPropertyCache(HealthProperties.BsfHealthMemoryParseReferenceEnabled, false)) {
            return;
        }
        for(int i=0;i<objInfo.getObjs().size();i++) {
            objInfo.getObjs().set(i, new PhantomReference(objInfo.getObjs().get(i), this.referenceQueue));
        }
        put(objInfo.getCls().getName(),objInfo.getObjs());
    }
    private void put(String clsName,List<Reference> list){
        if(map.get(clsName)==null){
            synchronized (lock) {
                if(map.get(clsName)==null) {
                    map.put(clsName,Collections.synchronizedList(new LinkedList()));
                }
            }
        }
        val ato = map.get(clsName);
        ato.addAll(list);
    }

    private void remove(String clsName,Reference obj){
        val ato = map.get(clsName);
        ato.remove(obj);
    }


    private void clearUpReference(boolean isWait){
        while (true) {
            try {
                Reference refer = null;
                if (isWait) {
                    refer = referenceQueue.remove();
                    if (refer == null)
                        continue;
                } else {
                    refer = referenceQueue.poll();
                    if (refer == null)
                        break;
                }
                Object obj = refer.get();
                if(obj==null){
                    obj=referentField.get(refer);
                }
                if (obj != null) {
                    refer.clear();
                    remove(obj.getClass().getName(), refer);
                    refer=null;
                }
            } catch (Exception e) {
                if (!(e instanceof InterruptedException)) {
                    LogUtils.error(ReferenceHashMap.class, "引用map资源回收异常:" + e.getMessage());
                }
            }
        }
    }

    public Map<String,Long> getReport(){
        //clearUpReference(false);
        Map<String,Long> rs = new HashMap<>();
        for(val kv:map.entrySet()){
            rs.put(kv.getKey(),new Long(kv.getValue().size()));
        }
        return rs;
    }

}
